/******************************************************************************
 * \brief	应用层协议-将调试信息从另一个设备/进程/CUP核心端进行输出，或从该端获取字符输入
 * \details	对SCP_PG_DEBUG这个组内的一些协议进行处理
 * \note	File format: UTF-8，中文编码：UTF-8
 * \author	将狼才鲸
 * \date	2023-03-24
 ******************************************************************************/

/*********************************** 头文件 ***********************************/
#include "scp_client_debug.h"
#include <string.h>	/* memcpy */
#include <stdio.h>	/* EOF vsnprintf */
#include <stdarg.h>	/* va_list va_start va_end */

/******************************** 模块私有变量 ********************************/
static scp_handle g_debug_scp = NULL;

/******************************** 模块私有变量 ********************************/
static char g_printf_text[1024];

/********************************** 私有函数 **********************************/
/**
 * \brief	在通信链路中实现printf
 * \details	使用可变参数
 */
static int scpclt_debug_vprintf(const char *fmt, va_list args)
{
	int text_len = (int)vsnprintf((char*)g_printf_text,
						sizeof(g_printf_text) - 4, fmt, args);

	if (text_len > 0) {
		scpclt_debug_puts(g_printf_text);
	}

	return text_len;
}

/********************************** 接口函数 **********************************/
/**
 * \brief	将传输连接和调试相关的几个应用层协议进行绑定，保持当次传输的连接
 */
void scpclt_debug_settarget(scp_handle hdl)
{
	g_debug_scp = hdl;
}

/**
 * \brief	向服务器发送消息
 * \details	该消息会得到服务器的接收确认
 * \param	str:	要发送的消息内容
 */
int scpclt_debug_puts(const char *str)
{
	int ack, res = 0;
	long len;
	seg_pkt_head_t *pkt;	/* 消息包头 */
	segment_t *seg = NULL;	/* segment片段，一条通信消息 */

	scp_t *pscp = (scp_t *)g_debug_scp;	/* 通信句柄，通信结构体 */
	if (!scp_handle_valid(pscp))		/* 通信结构体是否有效 */
		return EOF;

	seg = scpseg_new(pscp->pdev);		/* 申请通信片段（一条消息）结构体 */
	if (!seg)
		return EOF;

	pkt = (seg_pkt_head_t *)scpseg_data(seg);	/* 从通信片段segment_t中获取名为private_data的seg_pkt_head_t成员 */

	res = strlen(str);	/* 消息字符串的长度 */
	len = res + 1;		/* strlen获取到的长度比sizeof()少一个\0字符串结尾，这里要加上 */

	/** 1. 赋值包头 */
	pkt->TYPE= SCP_TYPE;					/* 传输层协议类型，当前就固定只支持SCP私有通信协议 */
	pkt->SEC = SEC_PW;						/* 权限写，用户组为单次连接级别 */
	pkt->PID = htons(SCP_PG_DEBUG_PUTS);	/* 应用层协议类型ID */
	pkt->LEN = htonl(len);					/* 要发送的有效消息长度 */

	/** 2. 赋值包数据，包头后直接跟着有效数据 */
	memcpy(((unsigned char *)pkt) + SEG_PKT_HEAD_LEN, str, len);

	seg->LEN = SEG_PKT_HEAD_LEN + len;	/* 消息长度为：包头 + 有效数据 */
	seg->TT = TT_ACKONLY;				/* 只需对请求进行响应，无需回复数据 */

	/** 3. 发送数据包并等待包响应，在里面轮询并阻塞，直到获取到响应包为止 */
	ack = scpclt_send_packet(pscp, seg);
	if (ack != ACK_ACK1 && ack != ACK_ACK2 && ack != ACK_ACK3) {
		/* 如果未收到正确的响应包包 */
		res = EOF;
	}

	if (seg->respseg && seg->respseg != seg)
		scpseg_free(seg->respseg);	/* 如果收到响应则删除该响应包 */

	scpseg_free(seg);	/* 将已发送包释放掉 */

	return res;
}

/**
 * \brief	在通信链路中通过printf打印字符串
 * \details	...是可变参数的用法
 */
int scpclt_debug_printf(const char *fmt, ...)
{
	va_list args;
	int i;

	va_start(args, fmt);
	i = scpclt_debug_vprintf(fmt, args);
	va_end(args);

	return i;
}

/**
 * \brief   获取服务器端的字符输入（键值）
 * \return  返回字符（键值）
 */
long scpclt_debug_getchar(void)
{
	long res = 0;
	uint8_t ack, *pktdat;
	seg_pkt_head_t *pkt;
	segment_t *seg;

	scp_t *pscp = (scp_t *)g_debug_scp;
	if (!scp_handle_valid(pscp))
		return -EACCES;

	seg = scpseg_new(pscp->pdev);
	if (seg == NULL)
		return -ENOMEM;

	pkt = (seg_pkt_head_t *)scpseg_data(seg);
	pkt->TYPE= SCP_TYPE;	/* segment type */
	pkt->SEC = SEC_PW;		/* segment security */
	pkt->PID = htons(SCP_PG_DEBUG_GETCHAR);	/* protocol id */
	pkt->LEN = 0;

	seg->LEN = SEG_PKT_HEAD_LEN;
	seg->TT = TT_RESPONDS;
	ack = scpclt_send_packet(pscp, seg);

	res = 0;
	if (ack == ACK_ACK1 || ack == ACK_ACK2 || ack == ACK_ACK3) {
		if (seg->respseg) {
			pkt = (seg_pkt_head_t *)scpseg_data(seg->respseg);
			pktdat = ((unsigned char *)pkt) + SEG_PKT_HEAD_LEN;
			res = *((long *)pktdat);
			res = ntohl(res);
		} else {
			res = -EFAULT;
		}
	} else {
		res = -EBUSY;
	}

	if (seg->respseg != NULL && seg->respseg != seg)
		scpseg_free(seg->respseg);
	scpseg_free(seg);

	return res;
}

/*********************************** 文件尾 ***********************************/
